package nebula

import (
	"net"

	"gddgame.cc/galaxy/satellite"
	"gddgame.cc/galaxy/satellite/compress/snappy"
	"gddgame.cc/galaxy/satellite/transport/kcp"
	"gddgame.cc/galaxy/satellite/transport/tcp"
	"gddgame.cc/galaxy/satellite/transport/ws"
	"gddgame.cc/galaxy/utils/cache"

	"gddgame.cc/galaxy/utils/def"
)

type Next func(err error) error
type NamespaceHandler func(NS) Namespace

type Server struct {
	logger def.Logger

	cache *cache.MemoryCache

	debug bool

	gateway *satellite.Gateway

	options *Options

	store      def.Store
	sessionKey string

	//certFile string
	//keyFile  string

	root *serverNamespace
}

type Options struct {
	WebPath string `json:"web_path"`
	TrackId string `json:"track_id"`
}

func NewServer(logger def.Logger, tcpAddr string, udpAddr string, options *Options) (*Server, error) {
	var e = &Server{}
	if options.WebPath == "" {
		options.WebPath = "/nebula.socket"
	}
	if options.TrackId == "" {
		options.TrackId = "trackId"
	}
	e.options = options
	e.gateway = satellite.NewGateway(e)
	if tcpAddr != "" {
		addrTcp, err := net.ResolveTCPAddr("tcp", tcpAddr)
		if err != nil {
			return nil, err
		}
		if err := e.gateway.AddTransport(tcp.NewTransport(addrTcp)); err != nil {
			return nil, err
		}
	}
	if udpAddr != "" {
		addrUdp, err := net.ResolveUDPAddr("udp", udpAddr)
		if err != nil {
			return nil, err
		}
		if err := e.gateway.AddTransport(kcp.NewTransport(addrUdp)); err != nil {
			return nil, err
		}
	}
	e.gateway.SetCompress(snappy.NewSnappy())

	e.root = newServerNamespace(e, NSP{})
	e.SetLogger(logger)
	return e, nil
}

func (e *Server) Attach(http ws.HttpMux) error {
	return e.gateway.AddTransport(ws.NewTransportServer(e.options.WebPath, http))
}
func (e *Server) Gateway() *satellite.Gateway {
	return e.gateway
}

func (e *Server) NewConn(session string) satellite.SessionConn {
	return newServerClient(e, e.gateway, session)
}

func (e *Server) GetSession(code byte, packet *satellite.Packet) (string, error) {
	t := MT(code)
	switch t {
	case P_OPEN:
		return "", nil
	case P_REOPEN:
		return packet.ReadString(), nil
	default:
		return "", nil
	}
}

func (e *Server) SetLogger(logger def.Logger) {
	e.logger = logger.LoggerWrap("satellite", "nebula")
	e.gateway.SetLogger(e.logger)
	e.cache = cache.Memory(e.logger)
}

func (e *Server) SetDebug(debug bool) {
	e.debug = debug
}

func (e *Server) Listen() error {
	e.logger.Debugf("[ Nebula ] Listen")
	err := e.gateway.Listen()
	if err != nil {
		return err
	}
	return nil
}

func (e *Server) Close() error {
	e.logger.Debugf("[ Nebula ] Close")
	err := e.gateway.Close()
	if err != nil {
		e.logger.Error(err)
	}
	err = e.root.Close()
	if err != nil {
		e.logger.Error(err)
	}
	return nil
}

func (e *Server) Of(ns NS) (Namespace, error) {
	return e.root.Of(ns)
}

func (e *Server) With(nsp NSP) (Namespace, error) {
	return e.root.With(nsp)
}

func (e *Server) Root() (Namespace, error) {
	return e.root, nil
}

func (e *Server) SetStore(store def.Store, sessionKey string) error {
	e.store = store
	e.sessionKey = sessionKey
	return nil
}

func (e *Server) ClientNum() int32 {
	return e.gateway.SessionNum()
}

func (e *Server) ConnNum() int32 {
	return e.gateway.ConnNum()
}

func (e *Server) Register(fn func(handler NamespaceHandler) error) func() error {
	return func() error {
		return fn(func(ns NS) Namespace {
			namespace, err := e.Of(ns)
			if err != nil {
				e.logger.Error("[ Nebula ] nebula namespace is exist", ns)
			} else {
				e.logger.Debugf("[ Nebula ] nebula namespace: %#v", ns)
			}
			return namespace
		})
	}
}
